package com.rzjm.exercise.common.utils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.io.Closeable;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.nio.charset.Charset;
import java.sql.Blob;
import java.sql.Clob;
import java.util.Collection;
import java.util.Map;

public abstract class ObjectUtils {
    /**
     * Test whether the given two objects are the same or not
     */
    public static final boolean equals(Object v1, Object v2) {
        if (v1 == null && (v2 == null || "".equals(v2))) {
            return true;
        } else if (v2 == null && "".equals(v1)) {
            return true;
        } else if (v1 == null || v2 == null) {
            return false;
        }
        
        Class<?> clazz1 = v1.getClass();
        Class<?> clazz2 = v2.getClass();
        
        if (!clazz1.equals(clazz2)) {
            return false;
        }
        
        if (Collection.class.isAssignableFrom(clazz1)) {
            long qty = ((Collection<?>) v1).stream().filter(c1 -> !((Collection<?>) v2).contains(c1)).count();
            
            if (qty > 0) {
                return false;
            }
            
            qty = ((Collection<?>) v2).stream().filter(c2 -> !((Collection<?>) v1).contains(c2)).count();
            
            if (qty > 0) {
                return false;
            }
            
            return true;
        }
        
        if (Map.class.isAssignableFrom(clazz1)) {
            long qty = ((Map<?, ?>) v1).entrySet().stream()
                    .filter(m1 -> ((Map<?, ?>) v2).entrySet().stream()
                            .filter(m2 -> m2.getKey().equals(m1.getKey()) && equals(m2.getValue(), m1.getValue()))
                            .count() == 0l)
                    .count();
            
            if (qty > 0) {
                return false;
            }
            
            qty = ((Map<?, ?>) v2).entrySet().stream()
                    .filter(m2 -> ((Map<?, ?>) v1).entrySet().stream()
                            .filter(m1 -> m1.getKey().equals(m2.getKey()) && equals(m1.getValue(), m2.getValue()))
                            .count() == 0l)
                    .count();
            
            if (qty > 0) {
                return false;
            }
            
            return true;
        }
        
        return v1.equals(v2);
    }
    
    /**
     * Test whether the given object is empty or not.
     */
    public static final boolean isEmpty(Object object) {
        if (object == null) {
            return true;
        }
        
        if ("".equals(object)) {
            return true;
        }
        
        Class<?> clazz = object.getClass();
        
        if (clazz.isArray()) {
            return Array.getLength(object) == 0;
        }
        
        if (Collection.class.isAssignableFrom(clazz)) {
            return ((Collection<?>) object).size() == 0;
        }
        
        if (Map.class.isAssignableFrom(clazz)) {
            return ((Map<?, ?>) object).size() == 0;
        }
        
        return false;
    }

    /**
     * 转换目标{@code T}实例为字符串，{@code null}转为{@code ""}<b>
     * 该方法主要用于在HTML(jsp)界面上显示
     * 
     * @param target 要转换的{@code T}实例
     * @return 已转换的字符串
     */
    public static final <T> String toString(final T target) {
        if (target == null) {
            return "";
        }
        
        return ConvertUtils.convert(target, String.class, "");
    }
    // -------------------------------------------------------------------------
    // Private Method
    // -------------------------------------------------------------------------
    private static final boolean cannotDisplayString(final Class<?> sourceClass) {
        return ServletRequest.class.isAssignableFrom(sourceClass) || ServletResponse.class.isAssignableFrom(sourceClass)
                || HttpSession.class.isAssignableFrom(sourceClass) || AutoCloseable.class.isAssignableFrom(sourceClass)
                || Closeable.class.isAssignableFrom(sourceClass) || Blob.class.isAssignableFrom(sourceClass)
                || Clob.class.isAssignableFrom(sourceClass) || byte[].class.isAssignableFrom(sourceClass)
                || Byte[].class.isAssignableFrom(sourceClass) || Charset.class.isAssignableFrom(sourceClass);
    }
    
    private static final String getThrowableDisplayString(final Throwable throwable) {
        final StringWriter stringWriter = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(stringWriter);
        throwable.printStackTrace(printWriter);
        
        return stringWriter.toString();
    }
}
